package elastic.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.css.sword.knowing.search.SearchField;
import com.css.sword.knowing.search.ValueRange;
import com.css.sword.knowing.search.comm.SearchConst;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author 作者 E-mail:caihx@css.com.cn
 * @date 创建时间：2018年8月20日 上午8:46:49
 * @version 1.0
 */
public class QueryBuilder {

	private static JSONObject queryParse(String query) {
		JSONObject json = null;
		try {
			json = (JSONObject) JSONObject.parse(query);

			return json;
		} catch (Exception e) {
			return null;
		}
	}

	public static List<SearchField> create(String query) {
		try {
			JSONObject json = queryParse(query);
			if (null == json)
				return null;

			return createWhere(json);
		} catch (Exception e) {
			return null;
		}
	}

	private static String getMethod(String method) {
		if (null == method || method.length() == 0)
			return SearchConst.SEARCH_METHOD_PHRASE;

		return method;
	}

	private static String getOperator(String operator) {
		if (null == operator || operator.length() == 0)
			return SearchConst.SEARCH_OPERATOR_MUST;

		return operator;
	}

	private static void setFieldExists(SearchField field, String fieldName, String operator) {
		field.setField(fieldName);
		field.setOperator(operator);
		field.setMethod(SearchConst.SEARCH_METHOD_EXISTS);
	}

	private static void setFieldBasic(SearchField field, String fieldName, JSONObject json) {
		field.setField(fieldName);
		field.setOperator(getOperator(json.getString("operator")));
		field.setMethod(getMethod(json.getString("method")));
	}

	private static void setFieldBasic(SearchField field, String fieldName,String method, String operator) {
		field.setField(fieldName);
		field.setOperator(operator);
		field.setMethod(method);
	}

	private static List<SearchField> createWhere(JSONObject json) {
		Set<String> keys = json.keySet();

		if (null == keys)
			return null;

		List<SearchField> searchFields = new ArrayList<SearchField>();

		for (String key : keys) {
			Object ob = json.get(key);

			if (null != ob) {
				switch (key.toLowerCase()) {
					case SearchConst.SEARCH_OPERATOR_MUST:
					case SearchConst.SEARCH_OPERATOR_SHOULD:
					case SearchConst.SEARCH_OPERATOR_MUST_NOT:
						if (ob instanceof List) {
							JSONArray arr = json.getJSONArray(key);

							for (int index = 0; index < arr.size(); index++) {
								SearchField of = new SearchField();
								of.setOperator(key.toLowerCase());

								JSONObject obj = arr.getJSONObject(index);
								if (null == obj)
									continue;

								List<SearchField> childFields = createWhere(obj);
								if (!childFields.isEmpty()) {
									of.setChildrenField(childFields);
									searchFields.add(of);
								}
							}
							continue;
						} else {
							JSONObject j = json.getJSONObject(key);
							SearchField of = new SearchField();
							of.setOperator(key.toLowerCase());
							List<SearchField> childFields = createWhere(j);
							if (!childFields.isEmpty()) {
								of.setChildrenField(childFields);
								searchFields.add(of);
							}
							continue;
						}
				}

				JSONObject j = json.getJSONObject(key);
				JSONObject nested = j.getJSONObject("nested");
				if (null != nested) {
					SearchField nf = new SearchField();
					nf.setNestedPath(key);
					nf.setOperator(getOperator(j.getString("operator")));

					List<SearchField> childFields = createWhere(nested);
					if(!childFields.isEmpty())
					{
						nf.setChildrenField(childFields);
						searchFields.add(nf);
					}
				} else {
					SearchField f = new SearchField();

					String method = getMethod(j.getString("method"));
					if (method.equalsIgnoreCase(SearchConst.SEARCH_METHOD_RANGE)) {
						JSONObject rangJson1 = j.getJSONObject("val");
						JSONObject rangJson2 = j.getJSONObject("val2");

						ValueRange valueRange = new ValueRange();
						boolean flag = false;

						if (null != rangJson1) {
							Set<String> sets = rangJson1.keySet();
							if(!sets.isEmpty())
							{
								String rkey = sets.iterator().next();
								valueRange.setComparison1(rkey);
								valueRange.setValue1(rangJson1.get(rkey));
								flag = true;
							}
						}
						if (null != rangJson2) {
							Set<String> sets = rangJson2.keySet();
							if(!sets.isEmpty())
							{
								String rkey = sets.iterator().next();
								valueRange.setComparison2(rkey);
								valueRange.setValue2(rangJson2.get(rkey));
								flag = true;
							}
						}
						if (flag) {
							f.setValueRanges(valueRange);
							setFieldBasic(f, key, j);
							searchFields.add(f);
						}
					} else {
						Object obj = j.get("val");
						if(null == obj)
							continue;

						if(obj instanceof List)
						{
							List<SearchField> sfs = new ArrayList<SearchField>();
							for(Object o : (List)obj)
							{
								SearchField multiField = new SearchField();
								multiField.setSearchValue(o);

								if(SearchConst.SEARCH_OPERATOR_MUST_NOT.equalsIgnoreCase(getOperator(j.getString("operator"))))
									setFieldBasic(multiField, key, getMethod(j.getString("method")), getOperator(j.getString("operator")));
								else
									setFieldBasic(multiField, key, getMethod(j.getString("method")), SearchConst.SEARCH_OPERATOR_SHOULD);
								sfs.add(multiField);
							}

							f.setChildrenField(sfs);
							f.setOperator(getOperator(j.getString("operator")));
						}
						else
						{
							if(obj instanceof String)
							{
								String val = String.valueOf(obj);
								if(val.equalsIgnoreCase("IS NULL"))
								{
									setFieldExists(f, key, SearchConst.SEARCH_OPERATOR_MUST_NOT);
								}
								else if(val.equalsIgnoreCase("NOT NULL"))
								{
									setFieldExists(f, key, SearchConst.SEARCH_OPERATOR_MUST);
								}
								else
								{
									f.setSearchValue(obj);
									setFieldBasic(f, key, j);
								}
							}
							else
							{
								f.setSearchValue(obj);
								setFieldBasic(f, key, j);
							}
						}
						searchFields.add(f);
					}
				}
			}
		}
		return searchFields;
	}
}
